import { HttpException, HttpStatus, Injectable, Logger } from '@nestjs/common';
import { CreatePostDto } from './dto/create-post.dto';
import { UpdatePostDto } from './dto/update-post.dto';
import { InjectRepository } from '@nestjs/typeorm';
import { Brackets, Repository } from 'typeorm';
import { Posts, StatusContent } from './entities/post.entity';
import { TagsService } from '../tags/tags.service';
import { CategoriesService } from '../categories/categories.service';
import { FindList } from 'src/dto/find-limit-dto';
import * as dayjs from 'dayjs';
import { searchDto } from './dto/find-post.dto';

@Injectable()
export class PostsService {
  constructor(
    @InjectRepository(Posts)
    private readonly postsRepository: Repository<Posts>,
    private readonly categoryService: CategoriesService,
    private readonly tagsService: TagsService,
    private readonly logger: Logger,
  ) {}
  // 创建文章
  // posts.service.ts
  async create(user, post: CreatePostDto): Promise<string> {
    const { title } = post;
    if (!title) {
      throw new HttpException('缺少文章标题', HttpStatus.BAD_REQUEST);
    }

    const doc = await this.postsRepository.findOne({
      where: { title },
    });
    if (doc) {
      throw new HttpException('文章已存在', HttpStatus.BAD_REQUEST);
    }

    const { tags, category = 0, status, isRecommend } = post;

    // 根据分类id获取分类
    const categoryDoc = await this.categoryService.findById(category);
    if (!categoryDoc) {
      throw new HttpException('所传分类不存在', HttpStatus.BAD_REQUEST);
    }

    // 根据传入的标签id,如 `1,2`,获取标签
    const tagsArray = await this.tagsService.findByIds(tags);
    const postParam: Partial<Posts> = {
      ...post,
      isRecommend,
      category: categoryDoc,
      tags: tagsArray,
      author: user,
      status: 'draft',
    };
    // 判断状态，为publish则设置发布时间
    if (status === 'publish') {
      Object.assign(postParam, {
        publishTime: new Date(),
      });
    }
    const newPost: Posts = await this.postsRepository.create({
      ...postParam,
    });
    const created = await this.postsRepository.save(newPost);
    return created.id;
  }

  // 获取文章列表
  async findAll(query?: {
    [x: string]: any;
    pageNum?: number;
    pageSize?: number;
  }): Promise<FindList<Posts>> {
    // return `This action returns all posts`;
    const {
      pageNum = 1,
      pageSize = 10,
      status,
      title,
      sortBy,
      ...params
    } = query;

    const qb = this.postsRepository
      .createQueryBuilder('post')
      .leftJoinAndSelect('post.category', 'category')
      .leftJoinAndSelect('post.tags', 'tag')
      .leftJoinAndSelect('post.author', 'user')
      .orderBy('post.updateTime', 'DESC')
      .select([
        'post.id',
        'post.title',
        'post.count',
        'post.summary',
        'post.likeCount',
        'post.isRecommend',
        'post.status',
        'post.isCommentOpen',
        'post.coverUrl',
        'post.isTop',
        'post.publishTime',
        'post.createTime',
        'post.updateTime',
        'user.id',
        'user.nickname',
        'category.id',
        'category.name',
        'tag.id',
        'tag.name',
      ]);
    if (sortBy) {
      const sortByObject = JSON.parse(sortBy);
      Object.keys(sortByObject).forEach((key) =>
        qb.orderBy(`post.${key}`, sortByObject[key]),
      );
    }

    if (status) qb.andWhere(`post.status = :status`, { status: status });
    if (title) qb.andWhere(`post.title LIKE :title`, { title: `%${title}%` });
    if (params) {
      Object.keys(params).forEach((key) => {
        if (params[key])
          qb.andWhere(`post.${key} = :${key}`, { [key]: params[key] });
      });
    }
    const count = await qb.getCount();
    qb.take(pageSize);
    qb.skip(pageSize * (pageNum - 1));

    const posts = await qb.getMany();

    return { list: posts, count: count };
  }
  // 获取指定文章
  async findOne(id: string): Promise<Posts> {
    // return `This action returns a #${id} post`;
    return await this.postsRepository.findOne({ where: { id } });
  }
  // 更新文章
  async update(user, id: string, updatePostDto: UpdatePostDto): Promise<Posts> {
    // return `This action updates a #${id} post`;
    const postFindqb = await this.postsRepository
      .createQueryBuilder('post')
      .leftJoinAndSelect('post.author', 'user')
      .andWhere('post.id = :id', { id: id });
    const existPost = await postFindqb.getOne();
    if (!existPost) {
      throw new HttpException(`id为${id}的文章不存在`, HttpStatus.FORBIDDEN);
    }
    if (user.id !== existPost.author.id) {
      throw new HttpException(
        `非该文章作者，不允许进行更新操作`,
        HttpStatus.FORBIDDEN,
      );
    }
    const existCategory = await this.categoryService.findById(
      updatePostDto.category,
    );
    if (!existCategory) {
      throw new HttpException('所传分类不存在', HttpStatus.BAD_REQUEST);
    }
    const existTags = await this.tagsService.findByIds(updatePostDto.tags);
    const updatePost = this.postsRepository.merge(existPost, {
      ...updatePostDto,
      category: existCategory,
      tags: existTags,
    });
    return this.postsRepository.save(updatePost);
  }
  // 刪除文章
  async remove(id: string) {
    // return `This action removes a #${id} post`;
    const existPost = await this.postsRepository.findOne({ where: { id } });
    if (!existPost) {
      throw new HttpException(`id为${id}的文章不存在`, HttpStatus.FORBIDDEN);
    }
    return await this.postsRepository.remove(existPost);
  }
  async findById(id: string): Promise<any> {
    const qb = this.postsRepository
      .createQueryBuilder('post')
      .leftJoinAndSelect('post.category', 'category')
      .leftJoinAndSelect('post.tags', 'tag')
      .leftJoinAndSelect('post.author', 'user')
      .where('post.id=:id')
      .setParameter('id', id);

    const result = await qb.getOne();
    if (!result)
      throw new HttpException(`id为${id}的文章不存在`, HttpStatus.BAD_REQUEST);
    await this.postsRepository.update(id, { count: result.count + 1 });

    return result.toResponseObject();
  }
  async findByIdAdmin(id: string): Promise<any> {
    const qb = this.postsRepository
      .createQueryBuilder('post')
      .leftJoinAndSelect('post.category', 'category')
      .leftJoinAndSelect('post.tags', 'tag')
      .leftJoinAndSelect('post.author', 'user')
      .where('post.id=:id')
      .setParameter('id', id);

    const result = await qb.getOne();
    if (!result)
      throw new HttpException(`id为${id}的文章不存在`, HttpStatus.BAD_REQUEST);

    return result.toResponseObject();
  }

  async search(query?: searchDto) {
    // async search(keyword?: string, tags?: number[], category?: number) {
    const {
      keyword = '',
      tags = [],
      category = null,
      status,
      title,
      pageNum = 1,
      pageSize = 10,
      sortBy,
    } = query;
    const qb = this.postsRepository
      .createQueryBuilder('post')
      .where('post.status = :status', { status: StatusContent.Publish });
    qb.take(pageSize);
    qb.skip(pageSize * (pageNum - 1));
    if (status) qb.andWhere(`post.status = :status`, { status: status });
    if (title) qb.andWhere(`post.title LIKE :title`, { title: `%${title}%` });
    if (sortBy) {
      const sortByObject = JSON.parse(sortBy);
      Object.keys(sortByObject).forEach((key) =>
        qb.orderBy(`post.${key}`, sortByObject[key]),
      );
    }
    // 根据关键字查询标题和内容
    if (keyword) {
      qb.andWhere(
        new Brackets((qb) => {
          qb.where('post.title LIKE :keyword').orWhere(
            'post.content LIKE :keyword',
          );
        }),
      ).setParameter('keyword', `%${keyword}%`);
    }
    // 根据标签查询
    if (tags.length) {
      qb.innerJoinAndSelect('post.tags', 'tags', 'tags.id in (:tags)', {
        tags,
      });
    } else {
      qb.leftJoinAndSelect('post.tags', 'tags');
    }

    // 根据分类查询
    if (category) {
      qb.innerJoinAndSelect(
        'post.category',
        'category',
        'category.id = :category',
        { category },
      );
    } else {
      qb.leftJoinAndSelect('post.category', 'category');
    }
    const list = await qb.getMany();
    const count = await qb.getCount();
    return { list: list, count: count };
  }

  async getArchive() {
    const query = this.postsRepository
      .createQueryBuilder('post')
      .where('post.status = :status', { status: StatusContent.Publish })
      .orderBy('post.createTime', 'DESC')
      .select(['post.id', 'post.title', 'post.createTime']);
    const allPost = await query.getMany();
    const dataObject = {};
    allPost.forEach((post) => {
      const year = dayjs(post.createTime).format('YYYY');
      if (!dataObject[year]) dataObject[year] = [];
      dataObject[year].push(post);
    });
    const data = [];
    for (const key in dataObject) {
      if (Object.prototype.hasOwnProperty.call(dataObject, key)) {
        const element = dataObject[key];
        data.unshift({
          year: key,
          list: element,
        });
      }
    }
    return data;
  }
}
